package com.zwh.se.constant_variable_operator;

/**
 * 探究一下计算机中的原码、反码、补码
 * 前提得先知道:
 * 对于正数：原码 = 反码 = 补码
 * 对于负数：原码取反 = 反码(符号位不变) ; 反码 + 1 = 补码
 * TIPS: 反码和补码的引用是为了让计算机能更简单地实现运算逻辑
 *
 * @author yexi
 */
public class Original_Inverse_Complement {

    public static void main(String[] args) {
        System.out.println("---------- 研究进制的转换和计算机的存储底层 ----------");
        //先稍微展示一下进制的转换：
        // 比如一个正整数 10
        // 通过10/2得到5，余0，则二进制右边第一位补0，然后5/2=2，余1，第二位是1，随后第三位2/2=1余0，剩下1，已经除尽了
        // 那么这个数10的二进制就是
        System.out.println(changeToBinary(10));//00000000000000000000000000001010
        // 当然正数反码补码与原码一样，所以价值不大，与反码补码关系密切的主要是负数
        // 比如一个负整数 -10
        // 道理与正数相同，唯一不一样的就是负数有一个 -号 ，那么怎么标记它是个负数呢，就是通过"符号位"，所谓的符号位，就是将第一个数变为1
        // 因此负数-10的二进制就是 10000000000000000000000000001010
        // 那么反码和补码是什么，现在就展示给你看
        System.out.println(changeToBinary(-10));//11111111111111111111111111110110
        // 11111111111111111111111111110110是怎么得来的？你先看-10的原码：10000000000000000000000000001010
        // 负数的原码很有意思，它正好是其对应正数的原码的符号位变成1即可。
        // 而负数的原码转反码，也就是除符号位以外其余位取反，也就是11111111111111111111111111110101，然后+1便得到了补码。
        // 那么我们就可以得出这样一个结论：计算机底层到底是怎么存储数据的呢?没错实际上就是以补码的形式进行存储的。

        System.out.println("---------- 研究二进制计算的溢出 ----------");
        //定义一个int类型的整数变量
        int num = Integer.MAX_VALUE;
        //01111111111111111111111111111111,其中首位的0就是符号位,代表这是一个正数
        System.out.println(changeToBinary(num));
        num = Integer.MIN_VALUE;
        //10000000000000000000000000000000,其中首位的1就是符号位,代表这是一个负数
        System.out.println(changeToBinary(num));
        //那么如果溢出了那怎么办?计算机会怎么进行取舍?
        num = Integer.MAX_VALUE;
        System.out.println(num);
        num++;
        //10000000000000000000000000000000,可以看到这就是溢出,由于符号位被用作充当进位了,导致整个数变成了负数
        System.out.println(num);
        System.out.println(changeToBinary(num));
        //那么负数出现溢出呢?
        num--;
        System.out.println(num);
        //01111111111111111111111111111111,可以看到负数计算的时候,由于符号位也用作了计算,因此也是会有这样的溢出的情况
        System.out.println(changeToBinary(num));
        //那如果是这样的情况呢?
        num = 0B11111111111111111111111111111111;
        System.out.println(num);
        num++;
        System.out.println(num);
        //00000000000000000000000000000000,符号位自己也被用作了进位的计算
        System.out.println(changeToBinary(num));
        num--;
        System.out.println(changeToBinary(-1));
        System.out.println(num);
        //11111111111111111111111111111111
        System.out.println(changeToBinary(num));

        System.out.println("---------- 研究计算机为什么要以补码作为存储底层 ----------");
        // 好，上面就是对于计算机底层所存储的补码和运算结果的研究，那么现在我们思考一下，为什么要引入这个概念？
        // 请看下面:
        System.out.println(changeToBinary(1));//00000000000000000000000000000001
        System.out.println(changeToBinary(-1));//11111111111111111111111111111111
        //那么当它们相加会发生什么？
        // 1 + (-1) = 1 - 1
        System.out.println(0B00000000000000000000000000000001 - 0B00000000000000000000000000000001);//0
        System.out.println(0B00000000000000000000000000000001 + 0B11111111111111111111111111111111);//0
        //是的，正数的补码 + 负数的补码 就能达成一个跟减法同等的效果，这就是引入补码的原因
        //这样的话，就能在硬件设计的时候，直接舍弃掉减法器的设计，只需要一个加法器，就能达成所有的计算需求
        //而我们所看到的负数，是为了还原出其本身的值，而通过补码转回反码，再转回原码所展示出来的

    }

    /**
     * 将数字转成对应的2进制字符串
     */
    static String changeToBinary(Number num) {
        StringBuilder sb = new StringBuilder();
        //最后返回的长度
        int size = 0;
        if (num instanceof Integer) {
            size = Integer.SIZE;
        } else if (num instanceof Byte) {
            size = Byte.SIZE;
        } else if (num instanceof Short) {
            size = Short.SIZE;
        } else if (num instanceof Long) {
            size = Long.SIZE;
        } else if (num instanceof Float) {
            size = Float.SIZE;
            for (int i = 0; i < size - Integer.toBinaryString(Float.floatToIntBits((float) num)).length(); i++) {
                sb.append(0);
            }
            return sb + Integer.toBinaryString(Float.floatToIntBits((float) num));
        } else if (num instanceof Double) {
            size = Double.SIZE;
            for (int i = 0; i < size - Long.toBinaryString(Double.doubleToRawLongBits((double) num)).length(); i++) {
                sb.append(0);
            }
            return sb + Long.toBinaryString(Double.doubleToRawLongBits((double) num));
        }
        for (int i = 0; i < size - Long.toBinaryString(num.longValue()).length(); i++) {
            sb.append(0);
        }
        String binary = sb + Long.toBinaryString(num.longValue());
        //减去多出来的1
        return binary.substring(binary.length() - size);
    }
}
